// shard 通讯模块

import { LifeCycles, generateId } from '@/ayaka'
import { CreepShardData, InterShardMemory } from './types'

/**
 * 爬虫跨 shard 流程：
 * 1. 在距离星门 1 格时停下来等待，将自己的记忆存入 intershardMemory，同时指定目标 shard（第 1tick）
 * 2. 所有 shard 读取其余 shard 的 intershardMemory，如果发现该 shard 中有爬虫数据的目标 shard 是自己，则保存该数据到自身（第 2tick）
 * 3. 所有 shard 读取其余 shard 的 intershardMemory，如果发现该 shard 中有爬虫数据的目标是该 shard，说明数据已经传输到该 shard 了，删除数据（第 2tick），同时爬虫走到星门
 */

export default class ShardCommunication {
    /**
     * 初始化
     */
    static init() {
        const interShardMemory = (JSON.parse(InterShardMemory.getLocal()) || {}) as InterShardMemory
        if (!interShardMemory.shardName) {
            interShardMemory.shardName = Game.shard.name
        }
        if (!interShardMemory.creeps) {
            interShardMemory.creeps = {}
        }
        if (!interShardMemory.usedId) {
            interShardMemory.usedId = {}
        }
        global.interShardMemory = interShardMemory
    }

    /**
     * 记忆清理
     */
    static clean() {
        const interShardMemory = global.interShardMemory
        for (const creepName in interShardMemory.creeps) {
            const data = interShardMemory.creeps[creepName]
            // 超时删除
            data.timeout--
            if (data.timeout < 0) {
                delete interShardMemory.creeps[creepName]
            }
        }
        for (const id in interShardMemory.usedId) {
            if (interShardMemory.usedId[id] < 2000) {
                interShardMemory.usedId[id]--
                if (interShardMemory.usedId[id] < 0) {
                    delete interShardMemory.usedId[id]
                }
            } else if (interShardMemory.usedId[id] < Game.time) {
                delete interShardMemory.usedId[id]
            }
        }
    }

    /**
     * 开始传输
     */
    static transferShardInfo() {
        for (const shardName of ['shard0', 'shard1', 'shard2', 'shard3']) {
            if (shardName === Game.shard.name) continue
            const remoteShardMemory = (JSON.parse(InterShardMemory.getRemote(shardName) || '{}') ||
                {}) as InterShardMemory
            if (!remoteShardMemory.creeps) continue
            for (const creepName in remoteShardMemory.creeps) {
                const data = remoteShardMemory.creeps[creepName]
                if (data.toShard === Game.shard.name && data.id && !(data.id in global.interShardMemory.usedId)) {
                    // 发现其他 shard 有数据要传输到该 shard
                    global.interShardMemory.creeps[creepName] = data
                    global.interShardMemory.usedId[data.id] = Game.time + 1500
                } else if (data.toShard === shardName) {
                    // 目标 shard 已经接收到数据，删除当前shard数据
                    // delete global.interShardMemory.creeps[creepName]
                }
            }
        }
    }

    /**
     * 传输完毕，删除记忆
     */
    static deleteShardInfo() {
        for (const creepName in global.interShardMemory.creeps) {
            const data = global.interShardMemory.creeps[creepName]
            if (data.toShard === Game.shard.name) {
                const creep = Game.creeps[creepName]
                // 爬虫已经到该 shard 了
                if (creep) {
                    creep.memory = data.memory
                    delete global.interShardMemory.creeps[creepName]
                }
            }
        }
    }

    /**
     * 上传指定 shard 的记忆
     */
    static uploadMemory(creep: Creep, shard: string) {
        if (!creep) return
        const data: CreepShardData = {
            id: generateId(10),
            toShard: shard,
            memory: creep.memory,
            timeout: 20,
        }
        global.interShardMemory.creeps[creep.name] = data
    }

    /**
     * 检查目标 shard 是否有指定爬虫记忆
     */
    static isCreepMemoryReady(creep: Creep, shard: string) {
        if (!creep) return false
        const targetShardMemory = (JSON.parse(InterShardMemory.getRemote(shard) || '{}') || {}) as InterShardMemory
        if (
            !targetShardMemory.creeps ||
            !targetShardMemory.creeps[creep.name] ||
            targetShardMemory.creeps[creep.name].timeout <= 1
        ) {
            return false
        }
        return true
    }
}

/**
 * shard 通讯管理器
 */
const shardManager = function () {
    // 私服不考虑跨服
    if (!Game.cpu.generatePixel) return
    ShardCommunication.init()
    ShardCommunication.clean()
    ShardCommunication.transferShardInfo()
    ShardCommunication.deleteShardInfo()
    InterShardMemory.setLocal(JSON.stringify(global.interShardMemory))
}

/**
 * 保存跨 shard 信息
 */
const saveShardInfo = function (): void {
    if (!Game.cpu.generatePixel) return
    if (global.interShardMemory) {
        InterShardMemory.setLocal(JSON.stringify(global.interShardMemory))
        // @ts-ignore
        delete global.interShardMemory
    }
}

export const crossShard: LifeCycles = {
    tickStart: shardManager,
    tickEnd: saveShardInfo,
}
